package zasm.zasm.instruction;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import zasm.zasm.assembler.Evaluator;
import zasm.zasm.util.Pair;

public class InstructionSet {

	private final Evaluator	evaluator;
	private final boolean	msFirst;

	public InstructionSet(final Evaluator evaluator, final boolean msFirst) {
		this.evaluator = evaluator;
		this.msFirst = msFirst;
	}

	public void addInstruction(final Iterable<String> instruction, final byte[] opcode, final int nBytes,
			final String modOp, final int class_) {
		addInstruction(instruction, opcode, nBytes, modOp, class_, 0, 0);
	}

	public void addInstruction(final Iterable<String> instruction, final byte[] opcode, final int nBytes,
			final String modOp, final int class_, final int shift, final int or) {

	}

	private void writeInt(final byte[] opcode, final int value, final int start, final int end) {
		int shiftedValue = value;
		if (msFirst) {
			for (int i = end - 1; i >= start; i--) {
				opcode[i] = (byte) shiftedValue;
				shiftedValue >>= 8;
			}
		} else {
			for (int i = start; i < end; i++) {
				opcode[i] = (byte) shiftedValue;
				shiftedValue >>= 8;
			}
		}
	}

	private interface Instruction {

		byte[] assemble(final List<String> expressions);

	}

	private class NullaryInstruction implements Instruction {

		private final byte[]	opcode;

		public NullaryInstruction(final byte[] opcode) {
			this.opcode = opcode;
		}

		@Override
		public byte[] assemble(final List<String> expressions) {
			return opcode;
		}

	}

	private class UnaryInstruction implements Instruction {

		private final int		nBytes;
		private final byte[]	opcode;

		public UnaryInstruction(final int nBytes, final byte[] opcode) {
			this.nBytes = nBytes;
			this.opcode = opcode;
		}

		@Override
		public byte[] assemble(final List<String> expressions) {
			final byte[] result = Arrays.copyOf(opcode, nBytes);
			writeInt(result, evaluator.evaluateUnknownSign(expressions.get(0), nBytes - opcode.length), opcode.length,
					nBytes);
			return result;
		}

	}

	private class ZIXInstruction {

	}

	private class InstructionSetNode {

		private Instruction								leaf;
		private Map<String, InstructionSetNode>			literalBranches;
		private List<Pair<Pattern, InstructionSetNode>>	patternBranches;

		public InstructionSetNode() {}

		public Instruction get(final Iterator<String> iterator) {
			return get(iterator, null, null);
		}

		protected Instruction get(final Iterator<String> iterator, final String capture1, final String capture2) {
			if (!iterator.hasNext()) {
				if (leaf != null) {
					return leaf;
				}

			} else {
				final String next = iterator.next();

				if (literalBranches != null) {
					final InstructionSetNode node = literalBranches.get(next);
					if (node != null) {
						return node.get(iterator);
					}
				}

				if (patternBranches != null) {
					for (final Pair<Pattern, InstructionSetNode> pair : patternBranches) {
						final Matcher matcher = pair.getFirst().matcher(next);
						if (matcher.matches()) {

						}
					}
				}
			}

			throw new UnknownInstructionException();
		}

		protected void add();

	}

}
